home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / JComboBox.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  34.2 KB  |  1,077 lines  |  [TEXT/CWIE]

  1. /** 
  2.  * @(#)JComboBox.java    1.54 98/08/28
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package javax.swing;
  15.  
  16. import java.beans.*;
  17. import java.util.*;
  18.  
  19. import java.awt.*;
  20. import java.awt.event.*;
  21.  
  22. import java.io.Serializable;
  23. import java.io.ObjectOutputStream;
  24. import java.io.ObjectInputStream;
  25. import java.io.IOException;
  26.  
  27. import javax.swing.event.*;
  28. import javax.swing.plaf.*;
  29. import javax.swing.border.*;
  30.  
  31. import javax.accessibility.*;
  32.  
  33. /**
  34.  * Swing's implementation of a ComboBox -- a combination of a text field and 
  35.  * drop-down list that lets the user either type in a value or select it from 
  36.  * a list that is displayed when the user asks for it. The editing capability 
  37.  * can also be disabled so that the JComboBox acts only as a drop down list.
  38.  * <p>
  39.  * For the keyboard keys used by this component in the standard Look and
  40.  * Feel (L&F) renditions, see the
  41.  * <a href="doc-files/Key-Index.html#JComboBox">JComboBox</a> key assignments.
  42.  * <p>
  43.  * <strong>Warning:</strong>
  44.  * Serialized objects of this class will not be compatible with 
  45.  * future Swing releases.  The current serialization support is appropriate
  46.  * for short term storage or RMI between applications running the same
  47.  * version of Swing.  A future release of Swing will provide support for
  48.  * long term persistence.
  49.  *
  50.  * @beaninfo
  51.  *   attribute: isContainer false
  52.  *
  53.  * @version 1.54 08/28/98
  54.  * @author Arnaud Weber
  55.  */
  56. public class JComboBox extends JComponent 
  57.     implements ItemSelectable,ListDataListener,ActionListener, Accessible 
  58. {
  59.     /**
  60.      * @see #getUIClassID
  61.      * @see #readObject
  62.      */
  63.     private static final String uiClassID = "ComboBoxUI";
  64.  
  65.     protected ComboBoxModel    dataModel;
  66.     protected ListCellRenderer renderer;
  67.     protected ComboBoxEditor       editor;
  68.     protected int maximumRowCount = 8;
  69.     protected boolean isEditable  = false;
  70.     protected Object selectedItemReminder = null;
  71.     protected KeySelectionManager keySelectionManager = null;
  72.     protected String actionCommand = "comboBoxChanged";
  73.     protected boolean lightWeightPopupEnabled = true;
  74.  
  75.     /**
  76.      * Creates a JComboBox that takes its items from an existing ComboBoxModel.
  77.      *
  78.      * @param aModel the ComboBoxModel that provides the displayed list of items
  79.      */
  80.     public JComboBox(ComboBoxModel aModel) {
  81.         super();
  82.         setModel(aModel);
  83.         installAncestorListener();
  84.         setOpaque(true);
  85.         updateUI();
  86.     }
  87.  
  88.     /** 
  89.      * Creates a JComboBox that contains the elements in the specified array.
  90.      */
  91.     public JComboBox(final Object items[]) {
  92.         super();
  93.         setModel(new DefaultComboBoxModel(items));
  94.         installAncestorListener();
  95.         updateUI();
  96.     }
  97.  
  98.     /**
  99.      * Creates a JComboBox that contains the elements in the specified Vector.
  100.      */
  101.     public JComboBox(Vector items) {
  102.         super();
  103.         setModel(new DefaultComboBoxModel(items));
  104.         installAncestorListener();
  105.         updateUI();
  106.     }
  107.  
  108.     /**
  109.      * Creates a JComboBox with a default data model.
  110.      * The default data model is an empty list of objects. 
  111.      * Use <code>addItem</code> to add items.
  112.      */
  113.     public JComboBox() {
  114.         super();
  115.         setModel(new DefaultComboBoxModel());
  116.         installAncestorListener();
  117.         updateUI();
  118.     }
  119.  
  120.     protected void installAncestorListener() {
  121.         addAncestorListener(new AncestorListener(){
  122.                             public void ancestorAdded(AncestorEvent event){ hidePopup();}
  123.                            public void ancestorRemoved(AncestorEvent event){ hidePopup();}
  124.                            public void ancestorMoved(AncestorEvent event){ hidePopup();}});
  125.     }
  126.  
  127.     /**
  128.      * Sets the L&F object that renders this component.
  129.      *
  130.      * @param ui  the ComboBoxUI L&F object
  131.      * @see UIDefaults#getUI
  132.      *
  133.      * @beaninfo
  134.      *       expert: true
  135.      *  description: The ComboBoxUI implementation that defines the combo box look and feel.
  136.      */
  137.     public void setUI(ComboBoxUI ui) {
  138.         super.setUI(ui);
  139.     }
  140.  
  141.     /**
  142.      * Notification from the UIFactory that the L&F has changed. 
  143.      *
  144.      * @see JComponent#updateUI
  145.      */
  146.     public void updateUI() {
  147.         setUI((ComboBoxUI)UIManager.getUI(this));
  148.     }
  149.  
  150.  
  151.     /**
  152.      * Returns the name of the L&F class that renders this component.
  153.      *
  154.      * @return "ComboBoxUI"
  155.      * @see JComponent#getUIClassID
  156.      * @see UIDefaults#getUI
  157.      */
  158.     public String getUIClassID() {
  159.         return uiClassID;
  160.     }
  161.  
  162.  
  163.     /**
  164.      * Returns the L&F object that renders this component.
  165.      *
  166.      * @return the ComboBoxUI object that renders this component
  167.      */
  168.     public ComboBoxUI getUI() {
  169.         return (ComboBoxUI)ui;
  170.     }
  171.  
  172.     /**
  173.      * Sets the data model that the JComboBox uses to obtain the list of items.
  174.      *
  175.      * @param aModel the ComboBoxModel that provides the displayed list of items
  176.      * 
  177.      * @beaninfo
  178.      *        bound: true
  179.      *  description: Model that the combo box uses to get data to display.
  180.      */
  181.     public void setModel(ComboBoxModel aModel) {
  182.         ComboBoxModel oldModel = dataModel;
  183.         if ( dataModel != null )
  184.             dataModel.removeListDataListener(this);
  185.         dataModel = aModel;
  186.         firePropertyChange( "model", oldModel, dataModel);
  187.         dataModel.addListDataListener(this);
  188.         invalidate();
  189.     }
  190.  
  191.     /**
  192.      * Returns the data model currently used by the JComboBox.
  193.      *
  194.      * @return the ComboBoxModel that provides the displayed list of items
  195.      */
  196.     public ComboBoxModel getModel() {
  197.         return dataModel;
  198.     }
  199.  
  200.     /*
  201.      * Properties
  202.      */
  203.  
  204.     /**
  205.      * When displaying the popup, JComboBox choose to use a light weight popup if
  206.      * it fits. This method allows you to disable this feature. You have to do disable
  207.      * it if your application mixes light weight and heavy weights components.
  208.      *
  209.      * @beaninfo
  210.      *       expert: true
  211.      *  description: When set, disables using light weight popups.
  212.      */
  213.     public void setLightWeightPopupEnabled(boolean aFlag) {
  214.         lightWeightPopupEnabled = aFlag;
  215.     }
  216.  
  217.     /**
  218.      * Returns true if lightweight (all-Java) popups are in use,
  219.      * or false if heavyweight (native peer) popups are being used.
  220.      *
  221.      * @return true if lightweight popups are in use
  222.      */
  223.     public boolean isLightWeightPopupEnabled() { 
  224.         return lightWeightPopupEnabled;
  225.     }
  226.  
  227.     /**
  228.      * Determines whether the JComboBox field is editable. An editable JComboBox
  229.      * allows the user to type into the field or selected an item from the list
  230.      * to initialize the field, after which it can be edited. (The editing affects
  231.      * only the field, the list item remains intact.) A non editable JComboBox 
  232.      * displays the selected item inthe field, but the selection cannot be modified.
  233.      *
  234.      * @param aFlag a boolean value, where true indicates that the field is editable
  235.      * 
  236.      * @beaninfo
  237.      *    preferred: true
  238.      *  description: If true, the user can type a new value in the combo box.
  239.      */
  240.     public void setEditable(boolean aFlag) {
  241.         boolean didChange = aFlag != isEditable;
  242.         isEditable = aFlag;
  243.         if ( didChange ) {
  244.             firePropertyChange( "editable", !isEditable, isEditable );
  245.         }
  246.     }
  247.  
  248.     /**
  249.      * Returns true if the JComboBox is editable.
  250.      * 
  251.      * @return true if the JComboBox is editable, else false
  252.      */
  253.     public boolean isEditable() {
  254.         return isEditable;
  255.     }
  256.  
  257.     /**
  258.      * Sets the maximum number of rows the JComboBox displays.
  259.      * If the number of objects in the model is greater than count,
  260.      * the combo box uses a scrollbar.
  261.      *
  262.      * @param count an int specifying the maximum number of items to display
  263.      *              in the list before using a scrollbar
  264.      * @beaninfo
  265.      *    preferred: true
  266.      *  description: The maximum number of rows the popup should have
  267.      */
  268.     public void setMaximumRowCount(int count) {
  269.         int oldCount = maximumRowCount;
  270.         maximumRowCount = count;
  271.         firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
  272.     }
  273.  
  274.     /**
  275.      * Returns the maximum number of items the combo box can display 
  276.      * without a scrollbar
  277.      *
  278.      * @return an int specifying the maximum number of items that are displayed
  279.      *         in the list before using a scrollbar
  280.      */
  281.     public int getMaximumRowCount() {
  282.         return maximumRowCount;
  283.     }
  284.  
  285.     /**
  286.      * Sets the renderer that paints the item selected from the list in
  287.      * the JComboBox field. The renderer is used if the JComboBox is not
  288.      * editable. If it is editable, the editor is used to render and edit
  289.      * the selected item.
  290.      * <p>
  291.      * The default renderer displays a string, obtained
  292.      * by calling the selected object's <code>toString</code> method.
  293.      * Other renderers can handle graphic images and composite items.
  294.      * <p>
  295.      * To display the selected item, <code>aRenderer.getListCellRendererComponent</code>
  296.      * is called, passing the list object and an index of -1.
  297.      *  
  298.      * @param aRenderer  the ListCellRenderer that displays the selected item.
  299.      * @see #setEditor
  300.      * @beaninfo
  301.      *     expert: true
  302.      *  description: The renderer that paints the item selected in the list.
  303.      */
  304.     public void setRenderer(ListCellRenderer aRenderer) {
  305.         ListCellRenderer oldRenderer = renderer;
  306.         renderer = aRenderer;
  307.         firePropertyChange( "renderer", oldRenderer, renderer );
  308.         invalidate();
  309.     }
  310.  
  311.     /**
  312.      * Returns the renderer used to display the selected item in the JComboBox
  313.      * field.
  314.      *  
  315.      * @return  the ListCellRenderer that displays the selected item.
  316.      */
  317.     public ListCellRenderer getRenderer() {
  318.         return renderer;
  319.     }
  320.  
  321.     /**
  322.      * Sets the editor used to paint and edit the selected item in the JComboBox
  323.      * field. The editor is used only if the receiving JComboBox is editable. 
  324.      * If not editable, the combo box uses the renderer to paint the selected item.
  325.      *  
  326.      * @param anEditor  the ComboBoxEditor that displays the selected item
  327.      * @see #setRenderer
  328.      * @beaninfo
  329.      *    expert: true
  330.      *  description: The editor that combo box uses to edit the current value
  331.      */
  332.     public void setEditor(ComboBoxEditor anEditor) {
  333.         ComboBoxEditor oldEditor = editor;
  334.  
  335.         if ( editor != null )
  336.             editor.removeActionListener(this);
  337.         editor = anEditor;
  338.         if ( editor != null ) {
  339.             editor.addActionListener(this);
  340.         }
  341.         firePropertyChange( "editor", oldEditor, editor );
  342.     }
  343.  
  344.     /**
  345.      * Returns the editor used to paint and edit the selected item in the JComboBox
  346.      * field.
  347.      *  
  348.      * @return the ComboBoxEditor that displays the selected item
  349.      */
  350.     public ComboBoxEditor getEditor() {
  351.         return editor;
  352.     }
  353.  
  354.     /*
  355.      * Selection
  356.      */
  357.     /** 
  358.      * Sets the selected item in the JComboBox by specifying the object in the list.
  359.      * If <code>anObject</code> is in the list, the list displays with 
  360.      * <code>anObject</code> selected. If the object does not exist in the list,
  361.      * the default data model selects the first item in the list.
  362.      *
  363.      * @param anObject  the list object to select
  364.      * @beaninfo
  365.      *    preferred:   true
  366.      *    description: Sets the selected item in the JComboBox.
  367.      */
  368.     public void setSelectedItem(Object anObject) {
  369.         dataModel.setSelectedItem(anObject);
  370.     }
  371.  
  372.     /**
  373.      * Returns the currently selected item.
  374.      *
  375.      * @return  the currently selected list object from the data model
  376.      */
  377.     public Object getSelectedItem() {
  378.         return dataModel.getSelectedItem();
  379.     }
  380.  
  381.     /**
  382.      * Selects the item at index <code>anIndex</code>.
  383.      *
  384.      * @param anIndex an int specifying the list item to select, where 0 specifies
  385.      *                the first item in the list
  386.      * @beaninfo
  387.      *   preferred: true
  388.      *  description: The item at index is selected.
  389.      */
  390.     public void setSelectedIndex(int anIndex) {
  391.         int size = dataModel.getSize();
  392.  
  393.     if ( anIndex == -1 ) {
  394.         setSelectedItem( null );
  395.     }
  396.         else if ( anIndex < -1 || anIndex >= size ) {
  397.             throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
  398.     }
  399.     else {
  400.         setSelectedItem(dataModel.getElementAt(anIndex));
  401.     }
  402.     }
  403.  
  404.     /**
  405.      * Returns the index of the currently selected item in the list. The result is not
  406.      * always defined if the JComboBox box allows selected items that are not in the
  407.      * list. 
  408.      * Returns -1 if there is no selected item or if the user specified an item
  409.      * which is not in the list.
  410.      
  411.      * @return an int specifying the currently selected list item, where 0 specifies
  412.      *                the first item in the list, or -1 if no item is selected or if
  413.      *                the currently selected item is not in the list
  414.      */
  415.     public int getSelectedIndex() {
  416.         Object sObject = dataModel.getSelectedItem();
  417.         int i,c;
  418.         Object obj;
  419.  
  420.         for ( i=0,c=dataModel.getSize();i<c;i++ ) {
  421.             obj = dataModel.getElementAt(i);
  422.             if ( obj.equals(sObject) )
  423.                 return i;
  424.         }
  425.         return -1;
  426.     }
  427.  
  428.     /** 
  429.      * Adds an item to the item list.
  430.      * This method works only if the JComboBox uses the default data model.
  431.      * JComboBox uses the default data model when created with the 
  432.      * empty constructor and no other model has been set.
  433.      *
  434.      * @param anObject the Object to add to the list
  435.      */
  436.     public void addItem(Object anObject) {
  437.         checkMutableComboBoxModel();
  438.         ((MutableComboBoxModel)dataModel).addElement(anObject);
  439.     }
  440.  
  441.     /** 
  442.      * Inserts an item into the item list at a given index. 
  443.      * This method works only if the JComboBox uses the default data model.
  444.      * JComboBox uses the default data model when created with the 
  445.      * empty constructor and no other model has been set.
  446.      *
  447.      * @param anObject the Object to add to the list
  448.      * @param index    an int specifying the position at which to add the item
  449.      */
  450.     public void insertItemAt(Object anObject, int index) {
  451.         checkMutableComboBoxModel();
  452.         ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index);
  453.     }
  454.  
  455.     /** 
  456.      * Removes an item from the item list.
  457.      * This method works only if the JComboBox uses the default data model.
  458.      * JComboBox uses the default data model when created with the empty constructor
  459.      * and no other model has been set.
  460.      *
  461.      * @param anObject  the object to remove from the item list
  462.      */
  463.     public void removeItem(Object anObject) {
  464.         checkMutableComboBoxModel();
  465.         ((MutableComboBoxModel)dataModel).removeElement(anObject);
  466.     }
  467.  
  468.     /**  
  469.      * Removes the item at <code>anIndex</code>
  470.      * This method works only if the JComboBox uses the default data model.
  471.      * JComboBox uses the default data model when created with the 
  472.      * empty constructor and no other model has been set.
  473.      *
  474.      * @param anIndex  an int specifying the idex of the item to remove, where 0
  475.      *                 indicates the first item in the list
  476.      */
  477.     public void removeItemAt(int anIndex) {
  478.         checkMutableComboBoxModel();
  479.     ((MutableComboBoxModel)dataModel).removeElementAt( anIndex );
  480.     }
  481.  
  482.     /** 
  483.      * Removes all items from the item list.
  484.      * This method works only if the JComboBox uses the default data model.
  485.      * JComboBox uses the default data model when created with the empty constructor
  486.      * and no other model has been set.
  487.      */
  488.     public void removeAllItems() {
  489.         checkMutableComboBoxModel();
  490.         MutableComboBoxModel model = (MutableComboBoxModel)dataModel;
  491.     int size = model.getSize();
  492.  
  493.     if ( model instanceof DefaultComboBoxModel ) {
  494.         ((DefaultComboBoxModel)model).removeAllElements();
  495.     }
  496.     else {
  497.         for ( int i = 0; i < size; ++i ) {
  498.             Object element = model.getElementAt( 0 );
  499.         model.removeElement( element );
  500.         }
  501.     }
  502.     }
  503.  
  504.     void checkMutableComboBoxModel() {
  505.         if ( !(dataModel instanceof MutableComboBoxModel) )
  506.             throw new InternalError("Cannot use this method with a non-Mutable data model.");
  507.     }
  508.  
  509.     /** 
  510.      * Causes the combo box to display its popup window 
  511.      * @see #setPopupVisible
  512.      */
  513.     public void showPopup() {
  514.         setPopupVisible(true);
  515.     }
  516.  
  517.     /** 
  518.      * Causes the combo box to close its popup window 
  519.      * @see #setPopupVisible
  520.      */
  521.     public void hidePopup() {
  522.         setPopupVisible(false);
  523.     }
  524.  
  525.     /**
  526.      * Set the visiblity of the popup
  527.      */
  528.     public void setPopupVisible(boolean v) {
  529.         getUI().setPopupVisible(this, v);
  530.     }
  531.     
  532.     /** 
  533.      * Determine the visibility of the popup
  534.      */
  535.     public boolean isPopupVisible() {
  536.     return getUI().isPopupVisible(this);
  537.     }
  538.  
  539.     /** Selection **/
  540.  
  541.     /** 
  542.      * Adds an ItemListener. <code>aListener</code> will receive an event when
  543.      * the selected item changes.
  544.      *
  545.      * @param aListener  the ItemListener that is to be notified
  546.      */
  547.     public void addItemListener(ItemListener aListener) {
  548.         listenerList.add(ItemListener.class,aListener);
  549.     }
  550.  
  551.     /** Removes an ItemListener
  552.      *
  553.      * @param aListener  the ItemListener to remove
  554.      */
  555.     public void removeItemListener(ItemListener aListener) {
  556.         listenerList.remove(ItemListener.class,aListener);
  557.     }
  558.  
  559.     /** 
  560.      * Adds an ActionListener. The listener will receive an action event
  561.      * the user finishes making a selection.
  562.      *
  563.      * @param l  the ActionListener that is to be notified
  564.      */
  565.     public void addActionListener(ActionListener l) {
  566.         listenerList.add(ActionListener.class,l);
  567.     }
  568.  
  569.     /** Removes an ActionListener 
  570.      *
  571.      * @param l  the ActionListener to remove
  572.      */
  573.     public void removeActionListener(ActionListener l) {
  574.         listenerList.remove(ActionListener.class,l);
  575.     }
  576.  
  577.     /** 
  578.      * Sets the action commnand that should be included in the event
  579.      * sent to action listeners.
  580.      *
  581.      * @param aCommand  a string containing the "command" that is sent
  582.      *                  to action listeners. The same listener can then
  583.      *                  do different things depending on the command it
  584.      *                  receives.
  585.      */
  586.     public void setActionCommand(String aCommand) {
  587.         actionCommand = aCommand;
  588.     }
  589.  
  590.     /** 
  591.      * Returns the action commnand that is included in the event sent to
  592.      *  action listeners.
  593.      *
  594.      * @return  the string containing the "command" that is sent
  595.      *          to action listeners.
  596.      */
  597.     public String getActionCommand() {
  598.         return actionCommand;
  599.     }
  600.  
  601.     /**
  602.      * Notify all listeners that have registered interest for
  603.      * notification on this event type.
  604.      *  
  605.      * @see EventListenerList
  606.      */
  607.     protected void fireItemStateChanged(ItemEvent e) {
  608.         // Guaranteed to return a non-null array
  609.         Object[] listeners = listenerList.getListenerList();
  610.         // Process the listeners last to first, notifying
  611.         // those that are interested in this event
  612.         for ( int i = listeners.length-2; i>=0; i-=2 ) {
  613.             if ( listeners[i]==ItemListener.class ) {
  614.                 // Lazily create the event:
  615.                 // if (changeEvent == null)
  616.                 // changeEvent = new ChangeEvent(this);
  617.                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
  618.             }
  619.         }
  620.     }   
  621.  
  622.     /**
  623.      * Notify all listeners that have registered interest for
  624.      * notification on this event type.
  625.      *  
  626.      * @see EventListenerList
  627.      */
  628.     protected void fireActionEvent() {
  629.         ActionEvent e = null;
  630.         // Guaranteed to return a non-null array
  631.         Object[] listeners = listenerList.getListenerList();
  632.         // Process the listeners last to first, notifying
  633.         // those that are interested in this event
  634.         for ( int i = listeners.length-2; i>=0; i-=2 ) {
  635.             if ( listeners[i]==ActionListener.class ) {
  636.                 if ( e == null )
  637.                     e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,getActionCommand());
  638.                 ((ActionListener)listeners[i+1]).actionPerformed(e);
  639.             }
  640.         }
  641.     }
  642.  
  643.     /**
  644.      * This method is called when the selected item changes. Its default implementation
  645.      *  notifies the item listeners
  646.      */
  647.     protected void selectedItemChanged() {
  648.         if ( selectedItemReminder != null ) {
  649.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  650.                                                selectedItemReminder,
  651.                                                ItemEvent.DESELECTED));
  652.         }
  653.  
  654.         selectedItemReminder = getModel().getSelectedItem();
  655.  
  656.         if ( selectedItemReminder != null )
  657.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  658.                                                selectedItemReminder,
  659.                                                ItemEvent.SELECTED));
  660.         fireActionEvent();
  661.     }
  662.  
  663.     /** 
  664.      * Returns an array containing the selected item. This method is implemented for 
  665.      * compatibility with ItemSelectable.
  666.      *
  667.      * @returns an array of Objects containing one element -- the selected item
  668.      */
  669.     public Object[] getSelectedObjects() {
  670.         Object selectedObject = getSelectedItem();
  671.         if ( selectedObject == null )
  672.             return new Object[0];
  673.         else {
  674.             Object result[] = new Object[1];
  675.             result[0] = selectedObject;
  676.             return result;
  677.         }
  678.     }
  679.  
  680.     /** This method is public as an implementation side effect. 
  681.      *  do not call or override. 
  682.      */
  683.     public void actionPerformed(ActionEvent e) {
  684.         Object newItem = getEditor().getItem();
  685.         getModel().setSelectedItem(newItem);
  686.         getUI().setPopupVisible(this, false);
  687.     }
  688.  
  689.     /** This method is public as an implementation side effect. 
  690.      *  do not call or override. 
  691.      *
  692.      * @see javax.swing.event.ListDataListener
  693.      */
  694.     public void contentsChanged(ListDataEvent e) {
  695.         ComboBoxModel mod = getModel();
  696.         Object newSelectedItem = mod.getSelectedItem();
  697.  
  698.     if ( selectedItemReminder == null ) {
  699.         if ( newSelectedItem != null )
  700.             selectedItemChanged();
  701.     }
  702.     else {
  703.         if ( !selectedItemReminder.equals(newSelectedItem) )
  704.             selectedItemChanged();
  705.     }
  706.  
  707.         if ( !isEditable() && newSelectedItem != null ) {
  708.             int i,c;
  709.             boolean shouldResetSelectedItem = true;
  710.             Object o;
  711.             Object selectedItem = mod.getSelectedItem();
  712.  
  713.             for ( i=0,c=mod.getSize();i<c;i++ ) {
  714.                 o = mod.getElementAt(i);
  715.                 if ( o.equals(selectedItem) ) {
  716.                     shouldResetSelectedItem = false;
  717.                     break;
  718.                 }
  719.             }
  720.  
  721.             if ( shouldResetSelectedItem ) {
  722.                 if ( mod.getSize() > 0 )
  723.                     setSelectedIndex(0);
  724.                 else
  725.                     setSelectedItem(null);
  726.             }
  727.         }
  728.     }
  729.  
  730.     /**
  731.      * Selects the list item that correponds to the specified keyboard character
  732.      * and returns true, if there is an item corresponding to that character.
  733.      * Otherwise, returns false.
  734.      *
  735.      * @param keyChar a char, typically this is a keyboard key typed by the user
  736.      */
  737.     public boolean selectWithKeyChar(char keyChar) {
  738.         int index;
  739.  
  740.         if ( keySelectionManager == null )
  741.             keySelectionManager = createDefaultKeySelectionManager();
  742.  
  743.         index = keySelectionManager.selectionForKey(keyChar,getModel());
  744.         if ( index != -1 ) {
  745.             setSelectedIndex(index);
  746.             return true;
  747.         }
  748.         else
  749.             return false;
  750.     }
  751.  
  752.     /**
  753.      * Invoked items have been added to the internal data model.
  754.      * The "interval" includes the first and last values added. 
  755.      *
  756.      * @see javax.swing.event.ListDataListener
  757.      */
  758.     public void intervalAdded(ListDataEvent e) {
  759.         contentsChanged(e);
  760.     }
  761.  
  762.     /**
  763.      * Invoked when values have been removed from the data model. 
  764.      * The"interval" includes the first and last values removed. 
  765.      *
  766.      * @see javax.swing.event.ListDataListener
  767.      */
  768.     public void intervalRemoved(ListDataEvent e) {
  769.         contentsChanged(e);
  770.     }
  771.  
  772.     /**
  773.      * Enables the combo box so that items can be selected. When the
  774.      * combo box is disabled, items cannot be selected and values
  775.      * cannot be typed into its field (if it is editable).
  776.      *
  777.      * @param b a boolean value, where true enables the component and
  778.      *          false disables it
  779.      * @beaninfo
  780.      *    preferred: true
  781.      *  description: Whether the combo box is enabled.
  782.      */
  783.     public void setEnabled(boolean b) {
  784.         super.setEnabled(b);
  785.         firePropertyChange( "enabled", !isEnabled(), isEnabled() );
  786.     }
  787.  
  788.     /**
  789.      * Initializes the editor with the specified item.
  790.      *                                 
  791.      * @param anEditor the ComboBoxEditor that displays the list item in the
  792.      *                 combo box field and allows it to be edited
  793.      * @param anItem   the object to display and edit in the field
  794.      */
  795.     public void configureEditor(ComboBoxEditor anEditor,Object anItem) {
  796.         anEditor.setItem(anItem);
  797.     }
  798.  
  799.     /**
  800.      * Handles KeyEvents, looking for the Tab key. If the Tab key is found,
  801.      * the popup window is closed.
  802.      *
  803.      * @param e  the KeyEvent containing the keyboard key that was pressed  
  804.      */
  805.     public void processKeyEvent(KeyEvent e) {
  806.         if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
  807.             hidePopup();
  808.         }
  809.         super.processKeyEvent(e);
  810.     }
  811.  
  812.     /**
  813.      * Returns true if the component can receive the focus. In this case,
  814.      * the component returns false if it is editable, so that the Editor
  815.      * object receives the focus, instead of the component.
  816.      *
  817.      * @return true if the component can receive the focus, else false. 
  818.      */
  819.     public boolean isFocusTraversable() {
  820.         return getUI().isFocusTraversable(this);
  821.     }
  822.  
  823.     /**
  824.      * Sets the object that translates a keyboard character into a list
  825.      * selection. Typically, the first selection with a matching first
  826.      * character becomes the selected item.
  827.      *
  828.      * @beaninfo
  829.      *       expert: true
  830.      *  description: The objects that changes the selection when a key is pressed.
  831.      */
  832.     public void setKeySelectionManager(KeySelectionManager aManager) {
  833.         keySelectionManager = aManager;
  834.     }
  835.  
  836.     /**
  837.      * Returns the list's key-selection manager.
  838.      *
  839.      * @return the KeySelectionManager currently in use
  840.      */
  841.     public KeySelectionManager getKeySelectionManager() {
  842.         return keySelectionManager;
  843.     }
  844.  
  845.     /* Accessing the model */
  846.     /**
  847.      * Returns the number of items in the list.
  848.      *
  849.      * @return an int equal to the number of items in the list
  850.      */
  851.     public int getItemCount() {
  852.         return dataModel.getSize();
  853.     }
  854.  
  855.     /**
  856.      * Returns the list item at the specified index.
  857.      *
  858.      * @param index  an int indicating the list position, where the first
  859.      *               item starts at zero
  860.      * @return the Object at that list position
  861.      */
  862.     public Object getItemAt(int index) {
  863.         return dataModel.getElementAt(index);
  864.     }
  865.  
  866.     /**
  867.      * Returns an instance of the default key-selection manager.
  868.      *
  869.      * @return the KeySelectionManager currently used by the list
  870.      * @see #setKeySelectionManager
  871.      */
  872.     protected KeySelectionManager createDefaultKeySelectionManager() {
  873.         return new DefaultKeySelectionManager();
  874.     }
  875.  
  876.  
  877.     /**
  878.      * The interface that defines a KeySelectionManager. To qualify as
  879.      * a KeySelectionManager, the class needs to implement the method
  880.      * that identifies the list index given a character and the 
  881.      * combo box data model.
  882.      */
  883.     public interface KeySelectionManager {
  884.         /** Given <code>aKey</code> and the model, returns the row
  885.          *  that should become selected. Return -1 if no match was
  886.          *  found. 
  887.          *
  888.          * @param  aKey  a char value, usually indicating a keyboard key that
  889.          *               was pressed
  890.          * @param aModel a ComboBoxModel -- the component's data model, containing
  891.          *               the list of selectable items 
  892.          * @return an int equal to the selected row, where 0 is the
  893.          *         first item and -1 is none. 
  894.          */
  895.         int selectionForKey(char aKey,ComboBoxModel aModel);
  896.     }
  897.  
  898.     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
  899.         public int selectionForKey(char aKey,ComboBoxModel aModel) {
  900.             int i,c;
  901.             int currentSelection = -1;
  902.             Object selectedItem = aModel.getSelectedItem();
  903.             String v;
  904.             String pattern;
  905.  
  906.             if ( selectedItem != null ) {
  907.                 selectedItem = selectedItem.toString();
  908.  
  909.                 for ( i=0,c=aModel.getSize();i<c;i++ ) {
  910.                     if ( selectedItem.equals(aModel.getElementAt(i).toString()) ) {
  911.                         currentSelection  =  i;
  912.                         break;
  913.                     }
  914.  
  915.                 }
  916.             }
  917.  
  918.             pattern = ("" + aKey).toLowerCase();
  919.             aKey = pattern.charAt(0);
  920.  
  921.             for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
  922.                 v = aModel.getElementAt(i).toString().toLowerCase();
  923.                 if ( v.length() > 0 && v.charAt(0) == aKey )
  924.                     return i;
  925.             }
  926.  
  927.             for ( i = 0 ; i < currentSelection ; i ++ ) {
  928.                 v = aModel.getElementAt(i).toString().toLowerCase();
  929.                 if ( v.length() > 0 && v.charAt(0) == aKey )
  930.                     return i;
  931.             }
  932.             return -1;
  933.         }
  934.     }
  935.  
  936.  
  937.     /** 
  938.      * See readObject() and writeObject() in JComponent for more 
  939.      * information about serialization in Swing.
  940.      */
  941.     private void writeObject(ObjectOutputStream s) throws IOException {
  942.         s.defaultWriteObject();
  943.     if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  944.         ui.installUI(this);
  945.     }
  946.     }
  947.  
  948.  
  949.     /**
  950.      * Returns a string representation of this JComboBox. This method 
  951.      * is intended to be used only for debugging purposes, and the 
  952.      * content and format of the returned string may vary between      
  953.      * implementations. The returned string may be empty but may not 
  954.      * be <code>null</code>.
  955.      * <P>
  956.      * Overriding paramString() to provide information about the
  957.      * specific new aspects of the JFC components.
  958.      * 
  959.      * @return  a string representation of this JComboBox.
  960.      */
  961.     protected String paramString() {
  962.     String selectedItemReminderString = (selectedItemReminder != null ?
  963.                          selectedItemReminder.toString() :
  964.                          "");
  965.     String isEditableString = (isEditable ?    "true" : "false");
  966.     String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
  967.                         "true" : "false");
  968.  
  969.     return super.paramString() +
  970.     ",isEditable=" + isEditableString +
  971.     ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
  972.     ",maximumRowCount=" + maximumRowCount +
  973.     ",selectedItemReminder=" + selectedItemReminderString;
  974.     }
  975.  
  976.  
  977. ///////////////////
  978. // Accessiblity support
  979. ///////////////////
  980.  
  981.     /**
  982.      * Get the AccessibleContext associated with this JComponent
  983.      *
  984.      * @return the AccessibleContext of this JComponent
  985.      */
  986.     public AccessibleContext getAccessibleContext() {
  987.         if ( accessibleContext == null ) {
  988.             accessibleContext = new AccessibleJComboBox();
  989.         }
  990.         return accessibleContext;
  991.     }
  992.  
  993.     /**
  994.      * The class used to obtain the accessible role for this object.
  995.      * <p>
  996.      * <strong>Warning:</strong>
  997.      * Serialized objects of this class will not be compatible with
  998.      * future Swing releases.  The current serialization support is appropriate
  999.      * for short term storage or RMI between applications running the same
  1000.      * version of Swing.  A future release of Swing will provide support for
  1001.      * long term persistence.
  1002.      */
  1003.     protected class AccessibleJComboBox extends AccessibleJComponent 
  1004.     implements AccessibleAction {
  1005.  
  1006.         /**
  1007.          * Get the role of this object.
  1008.          *
  1009.          * @return an instance of AccessibleRole describing the role of the
  1010.          * object
  1011.          * @see AccessibleRole
  1012.          */
  1013.         public AccessibleRole getAccessibleRole() {
  1014.             return AccessibleRole.COMBO_BOX;
  1015.         }
  1016.  
  1017.         /**
  1018.          * Get the AccessibleAction associated with this object if one
  1019.          * exists.  Otherwise return null.
  1020.          */
  1021.         public AccessibleAction getAccessibleAction() {
  1022.             return this;
  1023.     }
  1024.  
  1025.         /**
  1026.          * Return a description of the specified action of the object.
  1027.          *
  1028.          * @param i zero-based index of the actions
  1029.          */
  1030.         public String getAccessibleActionDescription(int i) {
  1031.             if (i == 0) {
  1032.                 // [[[PENDING:  WDW -- need to provide a localized string]]]
  1033.                 return new String("togglePopup");
  1034.             } else {
  1035.                 return null;
  1036.             }
  1037.         }
  1038.     
  1039.         /**
  1040.          * Returns the number of Actions available in this object.
  1041.          * If there is more than one, the first one is the "default"
  1042.          * action.
  1043.          *
  1044.          * @return the number of Actions in this object
  1045.          */
  1046.         public int getAccessibleActionCount() {
  1047.             return 1;
  1048.         }
  1049.  
  1050.         /**
  1051.          * Perform the specified Action on the object
  1052.          *
  1053.          * @param i zero-based index of actions
  1054.          * @return true if the the action was performed; else false.
  1055.          */
  1056.         public boolean doAccessibleAction(int i) {
  1057.             if (i == 0) {
  1058.                 setPopupVisible(!isPopupVisible());
  1059.                 return true;
  1060.             } else {
  1061.                 return false;
  1062.             }
  1063.         }
  1064.  
  1065. //        public Accessible getAccessibleAt(Point p) {
  1066. //            Accessible a = getAccessibleChild(1);
  1067. //            if ( a != null ) {
  1068. //                return a; // the editor
  1069. //            }
  1070. //            else {
  1071. //                return getAccessibleChild(0); // the list
  1072. //            }
  1073. //        }
  1074.  
  1075.     } // innerclass AccessibleJComboBox
  1076. }
  1077.